using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;

namespace Qibla.Library
{
    public class QiblaLib
    {
        /*
         * PointF s have been used to represent GPS coordinates and distance offsets
         * PointF.X represents the Longitude
         * PointF.Y represents the Lattitude         * 
         */

        private readonly Color DEFAULT_BACK_COLOR = Color.Beige;//Color.FromArgb(253, 255, 253);
        private readonly Color DEFAULT_MAKKAH_LINE_COLOR = Color.Lime;
        private readonly Color DEFAULT_NORTH_LINE_COLOR = Color.Red;
        private readonly Color DEFAULT_DIRECTION_COLOR = Color.Blue;
        private readonly Color DEFAULT_GRID_COLOR = Color.White;
        private const int EDGE_BUFFER = 20;
        private const int PROJECTION_VALUE = 85;

        private readonly PointD NORTH = new PointD(0, 90);
        private readonly PointD MAKKAH = 
            new PointD(39.8261797428131, 21.422469806126873);

        private Color _backColor;
        private Color _makkahLineColor;
        private Color _northLineColor;
        private Color _directionColor;
        private Color _gridColor;

        private double _distanceTravelled;
        private double _scalePixelsPerMeter;
        bool _showArrows = true;
                    
        /// <summary>
        /// Returns the distance travelled
        /// </summary>
        public double DistanceTravelled
        {
            get { return _distanceTravelled; }
        }
	

        /// <summary>
        /// Constructor
        /// </summary>
        public QiblaLib()
        {
            _directionColor = DEFAULT_DIRECTION_COLOR;
            _backColor = DEFAULT_BACK_COLOR;
            _northLineColor = DEFAULT_NORTH_LINE_COLOR;
            _makkahLineColor = DEFAULT_MAKKAH_LINE_COLOR;
            _gridColor = DEFAULT_GRID_COLOR;
        }

        /// <summary>
        /// Returns the scale to be displayed in the UI
        /// </summary>
        /// <returns></returns>
        public string GetScaleForDisplay()
        {
            return Math.Round(((1 / _scalePixelsPerMeter) * 100), 2) + " m"; // Because the line is 100 pixels
        }

        public Image DrawCompass(int screenHeight, int screenWidth, float heading, PointD currentPosition)
        {
            string debug = string.Empty;
            try
            {
debug = "begining";
                _scalePixelsPerMeter = 1;

                // Create the Bmp Canvas
                Image img = new Bitmap(screenWidth, screenHeight, PixelFormat.Format24bppRgb);
debug = "img created";

                // Create the graphics object to draw
                Graphics g = Graphics.FromImage(img);

                // Clear the contents of the canvas
                g.Clear(_backColor);
debug = "graphics cleared";

                PointD north = new PointD(currentPosition.Longitude, 90);
                PointD makkah = MAKKAH;

                // Find the center of the screen
                PointD screenCenter = new PointD();
                screenCenter.X = (int)(screenWidth / 2);
                screenCenter.Y = (int)(screenHeight / 2);

debug = "Found screen center";

                // if center x = currentPos.X
                // if center y = currentPos.Y
                // what is makkah x and y
                // I.e. find the offset coordinates of Makkah from the center (i.e. current pos)
                PointD offsetMakkah = GetOffsetCoordinateFromPoint(currentPosition, makkah);
                PointD relativeNorth = new PointD(0, NORTH.Lattitude);

debug = "found offset points";

                // Choose direction of travel so that it is up
                PointD projectedTravelPoint = new PointD(0, 90);

                // Find a close point along the directional line to Makkah
                PointD makkahDirectionalPoint = GetPointAlongLine(makkah, currentPosition);

debug = "found closer point along line";

                //Rotate Points by (-) heading so that direction of heading is up
                PointD rMakkah = RotatePointByDegrees(makkahDirectionalPoint, heading);
                PointD rNorth = RotatePointByDegrees(relativeNorth, heading);

debug = "rotated points";

                DrawGrid(g, screenCenter, screenHeight, screenWidth);

debug = "grid drawn";

                DrawDirectionsAndArrows2(screenHeight,
                    g,
                    screenCenter,
                    projectedTravelPoint,
                    rMakkah,
                    rNorth);

debug = "direactions and arrows drawn";

                DrawLegend(screenHeight, screenWidth, g);

debug = "legend drawn";

                g.Dispose();
                return img;
            }
            catch (Exception ex)
            {
                throw ex;
                //throw new Exception(debug);
                
            }
        }

        private PointD GetPointAlongLine(PointD point1, PointD point2)
        {
            double gradient = (point1.Y - point2.Y) / (point1.X - point2.X);

            PointD pointToPlot = new PointD();

            if (!Double.IsInfinity(gradient))
            {
                int directionFactor = (point2.Longitude > point1.Longitude) ? -1 : 1;
                int x = PROJECTION_VALUE * directionFactor;
                // Calculate y 
                double y = gradient * x;

                pointToPlot.X = x;
                pointToPlot.Y = y;
            }
            else
            {
                pointToPlot = point2;
            }

            return pointToPlot;
        }

        private void DrawDirection2(
            Graphics g,
            PointD origin,
            double scalePixelsPerMeter,
            Pen pen,
            PointD targetPoint)
        {
            // Find the gradient of the line
            PointD pointToPlot = GetScreenCoordinate(origin, targetPoint);

            //g.DrawLine(pen, origin.X, origin.Y, origin.X + movedPoint.X, origin.Y - movedPoint.Y);
            g.DrawLine(pen,
                Convert.ToInt32(origin.X),
                Convert.ToInt32(origin.Y),
                Convert.ToInt32(pointToPlot.X),
                Convert.ToInt32(pointToPlot.Y));
        }

        private void DrawDirectionsAndArrows2(int screenHeight, 
            Graphics g, 
            PointD mid, 
            PointD projectedTravelPoint, 
            PointD rMakkah, 
            PointD rNorth)
        {
            Pen pt = new Pen(_directionColor, 1);
            Pen pm = new Pen(_makkahLineColor, 1);
            Pen pn = new Pen(_northLineColor, 1);

            DrawDirection2(g, mid, _scalePixelsPerMeter, pt, projectedTravelPoint);
            DrawDirection2(g, mid, _scalePixelsPerMeter, pm, rMakkah);
            DrawDirection2(g, mid, _scalePixelsPerMeter, pn, rNorth);
            DrawOrigin(screenHeight, g, mid);

            ArrowHead arrow = new ArrowHead();
            arrow.p1 = new PointD(0, 86);
            arrow.p2 = new PointD(2, 84);
            arrow.p3 = new PointD(-2, 84);

            DrawArrowHead(screenHeight, g, mid, _scalePixelsPerMeter, pt, arrow);

            if (_showArrows)
            {
                // Calculate angle to Makkah
                double mAngle = CalculateAngle(new PointD(0, 0), rMakkah) * -1; // Has to rotate the opposite direction to that of travel
                ArrowHead mArrow = new ArrowHead();

                mArrow.p1 = RotatePoint(arrow.p1, mAngle);
                mArrow.p2 = RotatePoint(arrow.p2, mAngle);
                mArrow.p3 = RotatePoint(arrow.p3, mAngle);

                DrawArrowHead(screenHeight, g, mid, _scalePixelsPerMeter, pm, mArrow);

                // Calculate angle to North
                double nAngle = CalculateAngle(new PointD(0, 0), rNorth) * -1; // Has to rotate the opposite direction to that of travel

                ArrowHead nArrow = new ArrowHead();
                nArrow.p1 = RotatePoint(arrow.p1, nAngle);
                nArrow.p2 = RotatePoint(arrow.p2, nAngle);
                nArrow.p3 = RotatePoint(arrow.p3, nAngle);

                DrawArrowHead(screenHeight, g, mid, _scalePixelsPerMeter, pn, nArrow, true);
            }
            pt.Dispose();
            pm.Dispose();
            pn.Dispose();
        }

        /// <summary>
        /// Roteates a point by the given angle
        /// </summary>
        /// <param name="source">Point to rotate</param>
        /// <param name="angleRadians">Angle in degrees to rotate</param>
        /// <returns></returns>
        private PointD RotatePointByDegrees(PointD source, double angleInDegrees)
        {
            // Calculate the angle in radians
            double angleRadians = (Math.PI / 180) * angleInDegrees;
            PointD rotated = new PointD();
            rotated.X = ((source.X * Math.Cos(angleRadians)) - (source.Y * Math.Sin(angleRadians)));
            rotated.Y = ((source.X * Math.Sin(angleRadians)) + (source.Y * Math.Cos(angleRadians)));
            return rotated;
        }

        private PointD GetScreenCoordinate(PointD screenCenter, PointD sourcePoint)
        {
            // Screen
            // -->0+ x
            // |
            // |
            // v
            // +
            // y

            PointD pointToPlot = new PointD();
            pointToPlot.Y = (int)(screenCenter.Y - sourcePoint.Y);
            pointToPlot.X = (int)(screenCenter.X + sourcePoint.X);

            return pointToPlot;

        }

        /// <summary>
        /// Returns an offset point around another point
        /// </summary>
        /// <param name="pointToOffset"></param>
        /// <param name="offsetSource"></param>
        /// <returns></returns>
        private PointD GetOffsetCoordinateFromPoint(PointD pointToOffset, PointD makkah)
        {
            PointD offsetPoint = new PointD(
              makkah.Longitude - pointToOffset.Longitude,
              makkah.Lattitude - pointToOffset.Lattitude );

            return offsetPoint;
        }

        /// <summary>
        /// Returns a bmp with the direction of travel, direction to North, and direction
        /// to Makkah drawn
        /// </summary>
        /// <param name="screenHeight">Height of the Bmp Canvas</param>
        /// <param name="screenWidth">Width of the Bmp Canvas</param>
        /// <param name="start">Starting Coordinates</param>
        /// <param name="end">Ending Coordinates</param>
        /// <returns></returns>
        public Image DrawCompass(int screenHeight, int screenWidth, PointD start, PointD end)
        {
            // Create the Bmp Canvas
            Image img = new Bitmap(screenWidth, screenHeight, PixelFormat.Format24bppRgb);

            // Create the graphics object to draw
            Graphics g = Graphics.FromImage(img);

            // Clear the contents of the canvas
            g.Clear(_backColor);
            
            // Determine the starting location as the center (origin)
            PointD center = new PointD();
            center.X = start.X;
            center.Y = start.Y;

            // Find the center of the drawing canvas
            PointD mid = GetScreenCenter(screenHeight, screenWidth);

            // Draw the Grid
            DrawGrid(g, mid, screenHeight, screenWidth);

            // Calculate the offset to Makkah in meters from the current origin point
            PointD offsetDistanceMakkah = GetOffSetDistances(center, MAKKAH);

            // Calculate the offset to North in meters from the current origin point
            PointD offsetDistanceNorth = GetOffSetDistances(center, NORTH);

            // Calculate the offset of travel along the longitude and lattitude
            double longDistance = CDistanceBetweenLocations.Calc(
               0, start.X, 0, end.X);

            longDistance = longDistance * 1000; // Convert to meters

            double latDistance = CDistanceBetweenLocations.Calc(
                start.Y, 0, end.Y, 0);

            latDistance = latDistance * 1000; // Convert to meters

            // Since the distance travelled is always positive, determine direction of travel
            // and qualify Northernly travel as positive and easternly travel as positive

            if (start.Y > end.Y) //  North of Start is positive
            {
                latDistance = latDistance * -1;
            }

            if (start.X > end.X) // East of Start is Positive
            {
                longDistance = longDistance * -1;
            }

            PointD movedDistance = new PointD(longDistance, latDistance);

            // Calculate the Distance travelled
            _distanceTravelled = Math.Sqrt(Math.Pow(longDistance, 2.0) + Math.Pow(latDistance, 2.0));

            // Calculate the scale
            _scalePixelsPerMeter = CalculateScale(mid);

            // Calculate the angle to rotate the direction travelled so that it points up
            double angle;
            angle = CalculateAngle(new PointD(0, 0), movedDistance);

            // Rotate the Points
            PointD rMoved = RotatePoint(movedDistance, angle);
            PointD rMakkah = RotatePoint(offsetDistanceMakkah, angle);
            PointD rNorth = RotatePoint(offsetDistanceNorth, angle);

            // Draw the directions with the arrowheads
            DrawDirectionsAndArrows(screenHeight, g, mid, rMoved, rMakkah, rNorth);
            DrawPositions(screenHeight, screenWidth, g, start, end);
            DrawScaleLegendAndDistance(g, screenHeight, screenWidth);

            g.Dispose();
            return img;
        }

        private void DrawLegend(int screenHeight, int screenWidth, Graphics g)
        {
            Pen pM = new Pen(Color.Lime, 1);
            Pen pT = new Pen(Color.Blue, 1);
            Pen pN = new Pen(Color.Red, 1);

            int horizPos = screenWidth - 60;
            int vertPos = screenHeight - 35;
            g.DrawLine(pT, horizPos, vertPos, horizPos + 15, vertPos);
            g.DrawLine(pM, horizPos, vertPos + 12, horizPos + 15, vertPos + 12);
            g.DrawLine(pN, horizPos, vertPos + 24, horizPos + 15, vertPos + 24);

            Brush br = new SolidBrush(Color.Black);
            Font textFont = new Font(FontFamily.GenericSansSerif, 7F, FontStyle.Regular);
            Font titleFont = new Font(FontFamily.GenericSansSerif, 5.75F, FontStyle.Regular);
            
            g.DrawString("Direction of:", titleFont, br, new Rectangle(
                horizPos - 10, vertPos - 15, 60, 20));

            g.DrawString("Travel", textFont, br, new Rectangle(
                horizPos + 20, vertPos - 6, screenWidth, 20));

            g.DrawString("Makkah", textFont, br, new Rectangle(
               horizPos + 20, vertPos + 12 - 6, screenWidth, 20));

            g.DrawString("North", textFont, br, new Rectangle(
               horizPos + 20, vertPos + 24 - 6, screenWidth, 20));

            titleFont.Dispose();
            textFont.Dispose();
            br.Dispose();
            pM.Dispose();
            pT.Dispose();
            pN.Dispose();
        }        

        private void DrawDistanceTravelled(Graphics g, int screenWidth)
        {
            Brush br = new SolidBrush(Color.DarkGray);

            Font titleFont = new Font(FontFamily.GenericSansSerif, 5.75F, FontStyle.Regular);

            string distTxt = string.Format("Distance travelled: {0}m", Math.Round(_distanceTravelled, 2));
            g.DrawString(distTxt, titleFont, br, new Rectangle(
                3, 3, screenWidth, 20));

            br.Dispose();
            titleFont.Dispose();
        }

        private void DrawPositions(int screenHeight, int screenWidth, Graphics g, PointD start, PointD end)
        {
            Brush br = new SolidBrush(Color.DarkGray);

            Font titleFont = new Font(FontFamily.GenericSansSerif, 5.75F, FontStyle.Regular);

            string startTxt = "Start: " + start.ToString();
            string endTxt = "End: " + end.ToString();

            g.DrawString(startTxt, titleFont, br, new Rectangle(
                10, screenHeight - 68, screenWidth, 20));

            g.DrawString(endTxt, titleFont, br, new Rectangle(
                10, screenHeight - 60, screenWidth, 20));

            br.Dispose();
            titleFont.Dispose();
        }

        private void DrawScale(int screenHeight, Graphics g)
        {
            Brush br = new SolidBrush(Color.Black);
            Font titleFont = new Font(FontFamily.GenericSansSerif, 6.75F, FontStyle.Regular);
            Font scaleFont = new Font(FontFamily.GenericSansSerif, 7, FontStyle.Regular);
            g.DrawString("Scale:", titleFont, br, new Rectangle(
                10, screenHeight - 34, 110, 20));

            g.DrawString(GetScaleForDisplay(), scaleFont, br, new Rectangle(
                10, screenHeight - 20, 110, 20));

            Pen ps = new Pen(Color.Black, 1);
            g.DrawLine(ps, 10, screenHeight - 20, 110, screenHeight - 20);

            titleFont.Dispose();
            scaleFont.Dispose();
            br.Dispose();
            ps.Dispose();
        }

        private void DrawDirectionsAndArrows(int screenHeight, Graphics g, PointD mid, PointD rMoved, PointD rMakkah, PointD rNorth)
        {
            Pen pt = new Pen(_directionColor, 1);
            Pen pm = new Pen(_makkahLineColor, 1);
            Pen pn = new Pen(_northLineColor, 1);

            DrawDirection(g, mid, _scalePixelsPerMeter, pt, rMoved);
            DrawDirection(g, mid, _scalePixelsPerMeter, pm, rMakkah);
            DrawDirection(g, mid, _scalePixelsPerMeter, pn, rNorth);
            DrawOrigin(screenHeight, g, mid);

            ArrowHead arrow = new ArrowHead();
            arrow.p1 = new PointD(0, 86);
            arrow.p2 = new PointD(2, 84);
            arrow.p3 = new PointD(-2, 84);

            DrawArrowHead(screenHeight, g, mid, _scalePixelsPerMeter, pt, arrow);

            if (_showArrows)
            {
                // Calculate angle to Makkah
                double mAngle = CalculateAngle(new PointD(0, 0), rMakkah) * -1; // Has to rotate the opposite direction to that of travel
                ArrowHead mArrow = new ArrowHead();

                mArrow.p1 = RotatePoint(arrow.p1, mAngle);
                mArrow.p2 = RotatePoint(arrow.p2, mAngle);
                mArrow.p3 = RotatePoint(arrow.p3, mAngle);

                DrawArrowHead(screenHeight, g, mid, _scalePixelsPerMeter, pm, mArrow);

                // Calculate angle to North
                double nAngle = CalculateAngle(new PointD(0, 0), rNorth) * -1; // Has to rotate the opposite direction to that of travel

                ArrowHead nArrow = new ArrowHead();
                nArrow.p1 = RotatePoint(arrow.p1, nAngle);
                nArrow.p2 = RotatePoint(arrow.p2, nAngle);
                nArrow.p3 = RotatePoint(arrow.p3, nAngle);

                DrawArrowHead(screenHeight, g, mid, _scalePixelsPerMeter, pn, nArrow);
            }
            pt.Dispose();
            pm.Dispose();
            pn.Dispose();

        }

        /// <summary>
        /// Calculate the Scale
        /// </summary>
        /// <param name="mid"></param>
        /// <returns></returns>
        private double CalculateScale(PointD mid)
        {
            double scalePixelsPerMeter;
            scalePixelsPerMeter =
                (1 / (_distanceTravelled / (mid.Y - EDGE_BUFFER))); // Keep a buffer of 20 pixels from the edge
            return scalePixelsPerMeter;
        }

        /// <summary>
        /// Calculates the Center of the screen
        /// </summary>
        /// <param name="screenHeight">Screen Height</param>
        /// <param name="screenWidth">Screen Width</param>
        /// <returns></returns>
        private PointD GetScreenCenter(int screenHeight, int screenWidth)
        {
            PointD mid = new PointD();
            mid.X = screenWidth / 2;
            mid.Y = screenHeight / 2;
            return mid;
        }

        public Image GetBlankGrid(int screenHeight, int screenWidth)
        {
            // Create the Bmp Canvas
            Image img = new Bitmap(screenWidth, screenHeight, PixelFormat.Format24bppRgb);

            // Create the graphics object to draw
            Graphics g = Graphics.FromImage(img);

            // Clear the contents of the canvas
            g.Clear(_backColor);

            // Find the center of the drawing canvas
            PointD mid = GetScreenCenter(screenHeight, screenWidth);

            // Draw the Grid
            DrawGrid(g, mid, screenHeight, screenWidth);
            //DrawScaleLegendAndDistance(g, screenHeight, screenWidth);

            g.Dispose();

            return img;
        }

        /// <summary>
        /// Draws the Grid according to the scale
        /// </summary>
        /// <param name="g"></param>
        /// <param name="mid"></param>
        /// <param name="screenHeight"></param>
        /// <param name="screenWidth"></param>
        private void DrawGrid(Graphics g, PointD mid, int screenHeight, int screenWidth)
        {
            Pen p = new Pen(_gridColor, 1);

            for (int i = 0; i <= screenHeight; i += 50)
            {
                g.DrawLine(p, 0, (screenHeight / 2) + i, screenWidth, (screenHeight / 2) + i);
                g.DrawLine(p, 0, (screenHeight / 2) - i, screenWidth, (screenHeight / 2) - i);
            }

            for (int i = 0; i <= screenWidth; i += 50)
            {
                g.DrawLine(p, (screenWidth / 2) + i, 0, (screenWidth / 2) + i, screenHeight);
                g.DrawLine(p, (screenWidth / 2) - i, 0, (screenWidth / 2) - i, screenHeight);
            }

            g.DrawRectangle(p, new Rectangle(0, 0, screenWidth, screenHeight));
            p.Dispose();
        }

        private void DrawScaleLegendAndDistance(Graphics g, int screenHeight, int screenWidth)
        {
            DrawScale(screenHeight, g);
            DrawDistanceTravelled(g, screenWidth);
            DrawLegend(screenHeight, screenWidth, g);
        }

        /// <summary>
        /// Calculate the angle to rotate
        /// </summary>
        /// <param name="point1">Point 1</param>
        /// <param name="point2">Point 2</param>
        /// <returns></returns>
        private double CalculateAngle(PointD point1, PointD point2)
        {
            // First calculate the angle
            double height = point2.Y - point1.Y;
            double width = point2.X - point1.X;

            // tan (Tetha) = opposite/adjascent
            double angle = (180 / Math.PI) * Math.Atan(height / width);

            // Based on the quadrant determine which direction to rotate the line and 
            // by how much
            double quadrantFactor = -1;
            double direction = 0;

            if (height > 0 && width > 0)
            {
                quadrantFactor = -1;
                direction = 1;
            }

            if (height > 0 && width < 0)
            {
                direction = -1;
                quadrantFactor = 1;
            }

            if (height < 0 && width > 0)
            {
                direction = 1;
                quadrantFactor = -1;
            }

            if (height < 0 && width < 0)
            {
                direction = -1;
                quadrantFactor = +1;
            }

            angle = direction * (90 + (quadrantFactor * angle));

            double angleRadians = (Math.PI / 180) * angle;

            return angleRadians;
        }

        /// <summary>
        /// Draws a line from the origin to the given point, provided the scale
        /// </summary>
        /// <param name="g">Graphics object to draw with</param>
        /// <param name="origin">The origin Point</param>
        /// <param name="scalePixelsPerMeter">The scale</param>
        /// <param name="pen">The pen to use</param>
        /// <param name="targetPoint">The target Point</param>
        private void DrawDirection(
            Graphics g, 
            PointD origin, 
            double scalePixelsPerMeter, 
            Pen pen, 
            PointD targetPoint)
        {
            //Find number of pixels longitude 
            double numberOfLongPixels = targetPoint.X * scalePixelsPerMeter;
            double numberOfLatPixels = targetPoint.Y * scalePixelsPerMeter;
            PointD movedPoint = new PointD((float)numberOfLongPixels, (float)numberOfLatPixels);

            // If the x and y coordinates are in 6 digits find a closer point along the line
            if ((Math.Abs(movedPoint.X) > 100000) ||
                (Math.Abs(movedPoint.Y) > 100000))
            {
                // Find the gradient of the line
                double gradient = movedPoint.Y/ numberOfLongPixels;

                // Pick a value for that is 1/4th of its current value (say)
                double x = movedPoint.X / 1000;
                
                // Calculate y 
                double y = gradient * x;

                movedPoint.X =  x;
                movedPoint.Y =  y;
            }
            
            //g.DrawLine(pen, origin.X, origin.Y, origin.X + movedPoint.X, origin.Y - movedPoint.Y);
            g.DrawLine(pen,
                Convert.ToInt32(origin.X),
                Convert.ToInt32(origin.Y),
                Convert.ToInt32(origin.X + movedPoint.X),
                Convert.ToInt32(origin.Y - movedPoint.Y));
        }


        /// <summary>
        /// Returns the offset between two points in meters
        /// </summary>
        /// <param name="origin">The center point to calculate the offset from</param>
        /// <param name="target">The target point (offset point)</param>
        /// <returns></returns>
        private PointD GetOffSetDistances(PointD origin, PointD target)
        {
            PointD destinationOffset = new PointD();
            destinationOffset.Y = CDistanceBetweenLocations.Calc(
                origin.Y, 0, target.Y, 0);

            // Convert to metres
            destinationOffset.Y = destinationOffset.Y * 1000;

            if ((target.Y < origin.Y) && (destinationOffset.Y > 0))
            {
                destinationOffset.Y = destinationOffset.Y * -1;
            }

            destinationOffset.X = CDistanceBetweenLocations.Calc(
                0, origin.X, 0, target.X);

            // Convert to metres
            destinationOffset.X = destinationOffset.X * 1000;

            if ((target.X < origin.X) && (destinationOffset.X > 0))
            {
                destinationOffset.X = destinationOffset.X * -1;
            }

            return destinationOffset;
        }

        /// <summary>
        /// Roteates a point by the given angle
        /// </summary>
        /// <param name="source">Point to rotate</param>
        /// <param name="angleRadians">Angle in radians to rotate</param>
        /// <returns></returns>
        private PointD RotatePoint(PointD source, double angleRadians)
        {
            PointD rotated = new PointD();
            rotated.X = ((source.X * Math.Cos(angleRadians)) - (source.Y * Math.Sin(angleRadians)));
            rotated.Y = ((source.X * Math.Sin(angleRadians)) + (source.Y * Math.Cos(angleRadians)));
            return rotated;
        }

        public void Legend_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Pen pM = new Pen(Color.Lime, 1);
            Pen pT = new Pen(Color.Blue, 1);
            Pen pN = new Pen(Color.Red, 1);
            g.DrawLine(pT, 15, 20, 35, 20);
            g.DrawLine(pM, 15, 32, 35, 32);
            g.DrawLine(pN, 15, 45, 35, 45);

            pM.Dispose();
            pT.Dispose();
            pN.Dispose();
        }

        public void Scale_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            Pen pS = new Pen(Color.Black, 1);
            g.DrawLine(pS, 15, 20, 115, 20);
            pS.Dispose();
        }

        /// <summary>
        /// Draws the origin
        /// </summary>
        /// <param name="screenHeight"></param>
        /// <param name="g"></param>
        /// <param name="mid"></param>
        private void DrawOrigin(int screenHeight, Graphics g, PointD mid)
        {
            Pen blackPen = new Pen(Color.Black, 2);

            // g.DrawEllipse(blackPen, mid.X - 2, mid.Y - 2, 3, 3);
            g.DrawEllipse(blackPen, 
                Convert.ToInt32(mid.X - 2), 
                Convert.ToInt32(mid.Y - 2), 3, 3);
            blackPen.Dispose();
        }


        private void DrawArrowHead(int screenHeight, Graphics g, PointD mid, double scalePixelsPerMeter, Pen pn, ArrowHead a)
        {
            DrawArrowHead(screenHeight, g, mid, scalePixelsPerMeter, pn, a, false);
        }

        /// <summary>
        /// Draws the arrow head
        /// </summary>
        /// <param name="screenHeight"></param>
        /// <param name="g"></param>
        /// <param name="mid"></param>
        /// <param name="scalePixelsPerMeter"></param>
        /// <param name="pn"></param>
        /// <param name="a"></param>
        private void DrawArrowHead(int screenHeight, Graphics g, PointD mid, double scalePixelsPerMeter, Pen pn, ArrowHead a, bool isNorth)
        {
            //PointF pt1 = a.p1;
            //pt1.X += mid.X;
            //pt1.Y = screenHeight - mid.Y - pt1.Y;

            //PointF pt2 = a.p2;
            //pt2.X += mid.X;
            //pt2.Y = screenHeight - mid.Y - pt2.Y;

            //PointF pt3 = a.p3;
            //pt3.X += mid.X;
            //pt3.Y = screenHeight - mid.Y - pt3.Y;

            Point pt1 = new Point(
                Convert.ToInt32(a.p1.X), 
                Convert.ToInt32(a.p1.Y));
            pt1.X += Convert.ToInt32(mid.X);
            pt1.Y = Convert.ToInt32(screenHeight - mid.Y - pt1.Y);

            Point pt2 = new Point(
                Convert.ToInt32(a.p2.X), 
                Convert.ToInt32(a.p2.Y));
            pt2.X += Convert.ToInt32(mid.X);
            pt2.Y = Convert.ToInt32(screenHeight - mid.Y - pt2.Y);

            Point pt3 = new Point(
                Convert.ToInt32(a.p3.X),
                Convert.ToInt32(a.p3.Y));
            pt3.X += Convert.ToInt32(mid.X);
            pt3.Y = Convert.ToInt32(screenHeight - mid.Y - pt3.Y);

            g.DrawPolygon(pn, new Point[] { pt1, pt2, pt3 });

            if (isNorth)
            {
                Font titleFont = new Font(FontFamily.GenericSansSerif, 9F, FontStyle.Regular);

                Brush br = new SolidBrush(Color.Red);
                g.DrawString("N", titleFont, br, new Rectangle(
                pt3.X + 10, pt3.Y-10, pt3.X + 25, pt3.Y + 10));
                br.Dispose();
                titleFont.Dispose();
            }
        }
    }
}
